#pragma once
#include "ControlBase.h"

#define MIN_SCROLL_BLOCK 20

enum class ColumnType
{
	Text,
	Image,
	Check,
};
class GridViewColunm
{
public:
	std::wstring Name = L"";
	float Width = 120;
	ColumnType Type = ColumnType::Text;
};
class CellValue : public std::wstring
{
public:
	ID2D1Bitmap *Image;
	__int64 Tag;
	CellValue(std::wstring s) : std::wstring(s)
	{
	}
	CellValue(wchar_t *s) : std::wstring(s)
	{
	}
	CellValue(const wchar_t *s) : std::wstring(s)
	{
	}
	CellValue(ID2D1Bitmap *img) : std::wstring(L""), Image(img)
	{
	}
	CellValue(__int64 tag) : std::wstring(L""), Tag(tag)
	{
	}
	CellValue(bool tag) : std::wstring(L""), Tag(tag)
	{
	}
	CellValue(__int32 tag) : std::wstring(L""), Tag(tag)
	{
	}
	CellValue(unsigned __int32 tag) : std::wstring(L""), Tag(tag)
	{
	}
	CellValue(unsigned __int64 tag) : std::wstring(L""), Tag(tag)
	{
	}
};

class GridViewRow
{
public:
	List<CellValue> Cells = List<CellValue>();
	CellValue &operator[](int idx)
	{
		return Cells[idx];
	}
};
class GridView : public Control
{
public:
	UIClass Type() { return UIClass::UI_GridView; }
	GridView(int x = 0, int y = 0, int width = 120, int height = 20)
	{
		this->Location = POINT{x, y};
		this->Size = SIZE{width, height};
	}
	bool InScroll = false;
	ScrollChangedEvent ScrollChanged;
	List<GridViewColunm> Colunms = List<GridViewColunm>();
	List<GridViewRow> Rows = List<GridViewRow>();
	GridViewRow &operator[](int idx)
	{
		return Rows[idx];
	}
	float HeadHeight = 0.0f;
	float Boder = 1.5f;
	D2D1_COLOR_F HeadBackColor = Colors::Moccasin;
	D2D1_COLOR_F HeadForeColor = Colors::OrangeRed4;
	int ScrollRowPosition = 0;
	int ScrollColumnPosition = 0;
	int SelectedColunmIndex = -1;
	int SelectedRowIndex = -1;
	int UnderMouseColunmIndex = -1;
	int UnderMouseRowIndex = -1;
	D2D1_COLOR_F ButtonBackColor = Colors::GhostWhite;
	D2D1_COLOR_F ButtonCheckedColor = Colors::White;
	D2D1_COLOR_F SelectedItemBackColor = Colors::Blue;
	D2D1_COLOR_F SelectedItemForeColor = Colors::White;
	D2D1_COLOR_F UnderMouseItemBackColor = Colors::CadetBlue1;
	D2D1_COLOR_F UnderMouseItemForeColor = Colors::Black;
	D2D1_COLOR_F ScrollBackColor = Colors::LightGray;
	D2D1_COLOR_F ScrollForeColor = Colors::DimGrey;
	OnGridViewCheckStateChangedEvent OnGridViewCheckStateChanged;
	GridViewRow &SelectedRow()
	{
		static GridViewRow default_;
		if (this->SelectedRowIndex >= 0 && this->SelectedRowIndex < this->Rows.Count)
		{
			return this->Rows[this->SelectedRowIndex];
		}
		return default_;
	}
	std::wstring SelectedValue()
	{
		if (this->SelectedRowIndex >= 0 && this->SelectedRowIndex < this->Rows.Count)
		{
			return this->Rows[this->SelectedRowIndex].Cells[SelectedColunmIndex];
		}
		return L"";
	}
	void Clear()
	{
		this->Rows.Clear();
		this->ScrollRowPosition = 0;
	}

private:
#pragma region _GridView_
	POINT GetGridViewUnderMouseItem(int x, int y, GridView *ct)
	{
		float _render_width = ct->Width - 8;
		float _render_height = ct->Height;
		if (x > _render_width || y > _render_height)
			return {-1, -1};
		float font_height = Render->GetTextSize(L"I", ct->Font ? ct->Font : Render->DefaultFontObject).height;
		float row_height = font_height + 2.0f;
		float head_height = ct->HeadHeight == 0.0f ? row_height : ct->HeadHeight;
		if (y < head_height)
		{
			return {-1, -1};
		}
		auto font = ct->Font ? ct->Font : Render->DefaultFontObject;
		unsigned int s_x = ct->ScrollColumnPosition;
		unsigned int s_y = ct->ScrollRowPosition;
		float yf = ct->HeadHeight == 0.0f ? row_height : ct->HeadHeight;
		float xf = 0.0f;
		int xindex = -1;
		int yindex = -1;
		for (; s_x < ct->Colunms.Count; s_x++)
		{
			float c_width = ct->Colunms[s_x].Width;
			if (c_width + xf > _render_width)
			{
				c_width = _render_width - xf;
			}
			if (xf < x && xf + c_width > x)
			{
				xindex = s_x;
				break;
			}
			xf += ct->Colunms[s_x].Width;
			if (xf > _render_width)
			{
				break;
			}
		}
		if (((y - head_height) / row_height) + s_y < ct->Rows.Count)
		{
			yindex = ((y - head_height) / row_height) + s_y;
		}
		return {xindex, yindex};
	}
	D2D1_RECT_F GetGridViewScrollBlockRect(GridView *ct)
	{
		auto absloc = ct->AbsLocation;
		auto size = ct->Size;
		float _render_width = ct->Width - 8;
		float _render_height = ct->Height;
		float font_height = Render->GetTextSize(L"I", ct->Font ? ct->Font : Render->DefaultFontObject).height;
		float row_height = font_height + 2.0f;
		float head_height = ct->HeadHeight == 0.0f ? row_height : ct->HeadHeight;
		float render_items_height = _render_height - head_height;
		int render_items_count = render_items_height / row_height;
		if (render_items_count < ct->Rows.Count)
		{
			float scroll_block_height = (float)ct->Height * (float)render_items_count / (float)ct->Rows.Count;
			float scroll_block_top = ((float)ct->ScrollRowPosition / ((float)ct->Rows.Count /* - (float)render_items_count*/)) * (float)ct->Height;
			return {absloc.x + (ct->Width - 8.0f), absloc.y + scroll_block_top, 8.0f, scroll_block_height};
		}
		return {0, 0, 0, 0};
	}

	int GetGridViewRenderRowCount(GridView *ct)
	{
		float _render_height = ct->Height;
		float font_height = Render->GetTextSize(L"I", ct->Font ? ct->Font : Render->DefaultFontObject).height;
		float row_height = font_height + 2.0f;
		float head_height = ct->HeadHeight == 0.0f ? row_height : ct->HeadHeight;
		_render_height -= head_height;
		return (int)(_render_height / row_height);
	}
#pragma endregion
	void DrawScroll()
	{
		auto d2d = this->Render;
		auto abslocation = this->AbsLocation;
		auto font = this->Font ? this->Font : d2d->DefaultFontObject;
		auto size = this->ActualSize();

		if (this->Rows.Count > 0)
		{
			float _render_width = this->Width - 8;
			float _render_height = this->Height;
			float font_height = d2d->GetTextSize(L"I", font).height;
			float row_height = font_height + 2.0f;
			float head_height = this->HeadHeight == 0.0f ? row_height : this->HeadHeight;
			float render_items_height = _render_height - head_height;
			int render_items_count = render_items_height / row_height;
			if (render_items_count < this->Rows.Count)
			{
				int render_count = GetGridViewRenderRowCount(this);
				int max_scroll = this->Rows.Count - render_count;
				float scroll_block_height = ((float)render_count / (float)this->Rows.Count) * (float)this->Height;
				if (scroll_block_height < MIN_SCROLL_BLOCK)
					scroll_block_height = MIN_SCROLL_BLOCK;

				float scroll_block_move_space = this->Height - scroll_block_height;
				float yt = scroll_block_height * 0.5f;
				float yb = this->Height - (scroll_block_height * 0.5f);
				float per = (float)this->ScrollRowPosition / (float)max_scroll;
				float scroll_tmp_y = per * scroll_block_move_space;

				float scroll_block_top = scroll_tmp_y;
				d2d->FillRoundRect(abslocation.x + (this->Width - 8.0f), abslocation.y, 8.0f, this->Height, this->ScrollBackColor, 4.0f);
				d2d->FillRoundRect(abslocation.x + (this->Width - 8.0f), abslocation.y + scroll_block_top, 8.0f, scroll_block_height, this->ScrollForeColor, 4.0f);
			}
		}
	}
	void SetScrollByPos(float yof)
	{
		auto d2d = this->Render;
		auto abslocation = this->AbsLocation;
		auto font = this->Font ? this->Font : d2d->DefaultFontObject;
		auto size = this->ActualSize();
		int render_count = GetGridViewRenderRowCount(this);
		int max_scroll = this->Rows.Count - render_count;
		if (this->Rows.Count > 0)
		{
			float _render_width = this->Width - 8;
			float _render_height = this->Height;
			float font_height = d2d->GetTextSize(L"I", font).height;
			float row_height = font_height + 2.0f;
			float head_height = this->HeadHeight == 0.0f ? row_height : this->HeadHeight;
			float render_items_height = _render_height - head_height;
			int render_items_count = render_items_height / row_height;
			if (render_items_count < this->Rows.Count)
			{
				float scroll_block_height = ((float)render_count / (float)this->Rows.Count) * (float)this->Height;
				if (scroll_block_height < MIN_SCROLL_BLOCK)
					scroll_block_height = MIN_SCROLL_BLOCK;

				float yt = scroll_block_height * 0.5f;
				float yb = this->Height - (scroll_block_height * 0.5f);
				float per = (yof - yt) / (yb - yt);
				this->ScrollRowPosition = max_scroll * per;
			}
		}
		if (this->ScrollRowPosition < 0)
			this->ScrollRowPosition = 0;
		if (this->ScrollRowPosition > this->Rows.Count - render_count)
			this->ScrollRowPosition = this->Rows.Count - render_count;
		this->ScrollChanged(this);
	}

public:
	void Update() override
	{
		if (this->IsVisual == false)
			return;
		bool isUnderMouse = this->MostParent->UnderMouse == this;
		bool isSelected = this->MostParent->Selected == this;
		auto d2d = this->Render;
		auto abslocation = this->AbsLocation;
		auto size = this->ActualSize();
		auto absRect = this->AbsRect;
		d2d->PushDrawRect(absRect.left, absRect.top, absRect.right - absRect.left, absRect.bottom - absRect.top);
		{
			Control *tmpc = this;
			List<Control *> need_render_background;
			while (tmpc->BackColor.a < 1.0f)
			{
				tmpc = tmpc->Parent;
				need_render_background.Add(tmpc);
			}
			if (this->BackColor.a < 1.0f)
			{
				for (int i = need_render_background.Count - 1; i >= 0; i--)
				{
					d2d->FillRect(abslocation.x, abslocation.y, size.cx, size.cy, need_render_background[i]->BackColor);
					if (need_render_background[i]->Image)
					{
						need_render_background[i]->RenderImage();
					}
				}
			}
			d2d->FillRect(abslocation.x, abslocation.y, size.cx, size.cy, this->BackColor);
			if (this->Image)
			{
				this->RenderImage();
			}
			auto font = this->Font ? this->Font : d2d->DefaultFontObject;
			{
				float _render_width = this->Width - 8;
				float _render_height = this->Height;
				float font_height = d2d->GetTextSize(L"I", font).height;
				float row_height = font_height + 2.0f;

				unsigned int s_x = this->ScrollColumnPosition;
				unsigned int s_y = this->ScrollRowPosition;
				float head_height = this->HeadHeight == 0.0f ? row_height : this->HeadHeight;
				float yf = head_height;
				float xf = 0.0f;
				int i = s_x;
				for (; i < this->Colunms.Count; i++)
				{
					float c_width = this->Colunms[i].Width;
					if (c_width + xf > _render_width)
					{
						c_width = _render_width - xf;
					}
					float _r_height = row_height;
					auto ht = d2d->GetTextSize(this->Colunms[i].Name, font);
					float draw_x_offset = ht.width > c_width ? 1.0f : (c_width - ht.width) / 2.0f;
					float draw_y_offset = ht.height > head_height ? 1.0f : (head_height - ht.height) / 2.0f;

					d2d->PushDrawRect(abslocation.x + xf, abslocation.y, c_width, _r_height);
					{
						d2d->FillRect(abslocation.x + xf, abslocation.y, c_width, _r_height, this->HeadBackColor);
						d2d->DrawRect(abslocation.x + xf, abslocation.y, c_width, _r_height, this->HeadForeColor, 2.f);
						d2d->DrawString(this->Colunms[i].Name,
										abslocation.x + xf + draw_x_offset,
										abslocation.y + draw_y_offset,
										this->HeadForeColor, font);
					}
					d2d->PopDrawRect();
					xf += this->Colunms[i].Width;
					if (xf > _render_width)
					{
						break;
					}
				}
				xf = 0;
				i = 0;
				for (int r = s_y; r < this->Rows.Count && i < (int)(_render_height / row_height); r++, i++)
				{
					GridViewRow &row = this->Rows[r];
					float xf = 0.0f;
					for (int c = s_x; c < this->Colunms.Count; c++)
					{
						{
							float c_width = this->Colunms[c].Width;
							if (c_width + xf > _render_width)
							{
								c_width = _render_width - xf;
							}
							float _r_height = row_height;
							d2d->PushDrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height);
							{
								switch (this->Colunms[c].Type)
								{
								case ColumnType::Text:
								{
									if (c == this->SelectedColunmIndex && r == this->SelectedRowIndex)
									{
										d2d->FillRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->SelectedItemBackColor);
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->SelectedItemForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
											d2d->DrawString(row.Cells[c],
															abslocation.x + xf + 1.0f,
															abslocation.y + yf + 1.0f,
															this->SelectedItemForeColor, font);
									}
									else if (c == this->UnderMouseColunmIndex && r == this->UnderMouseRowIndex)
									{
										d2d->FillRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemBackColor);
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
											d2d->DrawString(row.Cells[c],
															abslocation.x + xf + 1.0f,
															abslocation.y + yf + 1.0f,
															this->UnderMouseItemForeColor, font);
									}
									else
									{
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->ForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
											d2d->DrawString(row.Cells[c],
															abslocation.x + xf + 1.0f,
															abslocation.y + yf + 1.0f,
															this->ForeColor, font);
									}
								}
								break;
								case ColumnType::Image:
								{
									float left = (c_width - row_height) / 2.0f;
									float top = 0.0f;
									float _rsize = row_height;
									if (left < 0.0f)
									{
										left = 0.0f;
										top = (row_height - c_width) / 2.0f;
										_rsize = c_width;
									}
									if (c == this->UnderMouseColunmIndex && r == this->UnderMouseRowIndex)
									{
										d2d->FillRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemBackColor);
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
										{
											d2d->DrawBitmap(row.Cells[c].Image,
															abslocation.x + xf + left,
															abslocation.y + yf + top,
															row_height, row_height);
										}
									}
									else
									{
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->ForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
										{
											d2d->DrawBitmap(row.Cells[c].Image,
															abslocation.x + xf + left,
															abslocation.y + yf + top,
															row_height, row_height);
										}
									}
								}
								break;
								case ColumnType::Check:
								{
									float left = (c_width - row_height) / 2.0f;
									float top = 0.0f;
									float _rsize = row_height;
									if (left < 0.0f)
									{
										left = 0.0f;
										top = (row_height - c_width) / 2.0f;
										_rsize = c_width;
									}
									if (c == this->UnderMouseColunmIndex && r == this->UnderMouseRowIndex)
									{
										d2d->FillRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemBackColor);
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->UnderMouseItemForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
										{
											d2d->DrawRect(
												abslocation.x + xf + left + (_rsize * 0.2),
												abslocation.y + yf + top + (_rsize * 0.2),
												_rsize * 0.6, _rsize * 0.6,
												this->ForeColor);
											if (row.Cells[c].Tag)
											{
												d2d->FillRect(
													abslocation.x + xf + left + (_rsize * 0.35),
													abslocation.y + yf + top + (_rsize * 0.35),
													_rsize * 0.3, _rsize * 0.3,
													this->ForeColor);
											}
										}
									}
									else
									{
										d2d->DrawRect(abslocation.x + xf, abslocation.y + yf, c_width, _r_height, this->ForeColor,
													  r == this->UnderMouseRowIndex ? 1.0f : 0.5f);
										if (row.Cells.Count > c)
										{
											d2d->DrawRect(
												abslocation.x + xf + left + (_rsize * 0.2),
												abslocation.y + yf + top + (_rsize * 0.2),
												_rsize * 0.6, _rsize * 0.6,
												this->ForeColor);
											if (row.Cells[c].Tag)
											{
												d2d->FillRect(
													abslocation.x + xf + left + (_rsize * 0.35),
													abslocation.y + yf + top + (_rsize * 0.35),
													_rsize * 0.3, _rsize * 0.3,
													this->ForeColor);
											}
										}
									}
								}
								break;
								default:
									break;
								}
							}
							d2d->PopDrawRect();
						}
						xf += this->Colunms[c].Width;
						if (xf > _render_width)
						{
							break;
						}
					}
					yf += row_height;
				}
				d2d->PushDrawRect(
					(float)abslocation.x,
					(float)abslocation.y,
					(float)size.cx,
					(float)size.cy);
				{
					if (this->MostParent->UnderMouse == this)
					{
						d2d->DrawRect(abslocation.x, abslocation.y, size.cx, size.cy, this->BolderColor, 4);
					}
					else
					{
						d2d->DrawRect(abslocation.x, abslocation.y, size.cx, size.cy, this->BolderColor, 2);
					}
				}
				d2d->PopDrawRect();
				this->DrawScroll();
			}
			d2d->DrawRect(abslocation.x, abslocation.y, size.cx, size.cy, this->BolderColor, this->Boder);
		}
		d2d->PopDrawRect();
	}
	void AutoSizeColumn(int col)
	{
		if (this->Colunms.Count > col)
		{
			auto d2d = this->Render;
			auto font = this->Font ? this->Font : d2d->DefaultFontObject;
			auto &column = this->Colunms[col];
			column.Width = 10.0f;
			for (int i = 0; i < this->Rows.Count; i++)
			{
				auto &r = this->Rows[i];
				if (r.Cells.Count > col)
				{
					auto width = font->GetTextSize(r.Cells[col].c_str()).width;
					if (column.Width < width)
					{
						column.Width = width;
					}
				}
			}
		}
	}
	bool ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam, int xof, int yof) override
	{
		switch (message)
		{
		case WM_DROPFILES:
		{
			HDROP hDropInfo = HDROP(wParam);
			UINT uFileNum = DragQueryFile(hDropInfo, 0xffffffff, NULL, 0);
			TCHAR strFileName[MAX_PATH];
			List<std::wstring> files;
			for (int i = 0; i < uFileNum; i++)
			{
				DragQueryFile(hDropInfo, i, strFileName, MAX_PATH);
				files.Add(strFileName);
			}
			DragFinish(hDropInfo);
			if (files.Count > 0)
			{
				this->OnDropFile(this, files);
			}
		}
		break;
		case WM_MOUSEWHEEL: // mouse wheel
		{
			bool need_update = false;
			if (GET_WHEEL_DELTA_WPARAM(wParam) < 0)
			{
				auto d2d = this->Render;
				auto font = this->Font ? this->Font : d2d->DefaultFontObject;
				float font_height = font->GetTextSize(L"I").height;
				float row_height = font_height + 2.0f;
				float head_height = this->HeadHeight == 0.0f ? row_height : this->HeadHeight;
				int pre_render_item_count = (this->Rows.Count - this->ScrollRowPosition) - 1;
				float pre_render_items_height = (pre_render_item_count * row_height) + head_height;
				if (pre_render_items_height + row_height > this->Height)
				{
					need_update = true;
					this->ScrollRowPosition += 1;
					this->ScrollChanged(this);
				}
			}
			else
			{
				if (this->ScrollRowPosition > 0)
				{
					need_update = true;
					this->ScrollRowPosition -= 1;
					this->ScrollChanged(this);
				}
			}
			auto undermouseindex = GetGridViewUnderMouseItem(xof, yof, this);
			this->UnderMouseColunmIndex = undermouseindex.x;
			this->UnderMouseRowIndex = undermouseindex.y;
			MouseEventArgs event_obj = MouseEventArgs(MouseButtons::MouseButtons_None, 0, xof, yof, GET_WHEEL_DELTA_WPARAM(wParam));
			this->OnMouseWheel(this, event_obj);
			if (need_update)
			{
				this->SingleUpdate();
			}
		}
		break;
		case WM_MOUSEMOVE: // mouse move
		{
			this->MostParent->UnderMouse = this;
			bool need_update = false;
			if (this->InScroll)
			{
				need_update = true;
				SetScrollByPos(yof);
			}
			else
			{
				auto undermouseindex = GetGridViewUnderMouseItem(xof, yof, this);
				if (this->UnderMouseColunmIndex != undermouseindex.x ||
					this->UnderMouseRowIndex != undermouseindex.y)
				{
					need_update = true;
				}
				this->UnderMouseColunmIndex = undermouseindex.x;
				this->UnderMouseRowIndex = undermouseindex.y;
			}
			MouseEventArgs event_obj = MouseEventArgs(MouseButtons::MouseButtons_None, 0, xof, yof, HIWORD(wParam));
			this->OnMouseMove(this, event_obj);
			if (need_update)
			{
				this->SingleUpdate();
			}
		}
		break;
		case WM_LBUTTONDOWN: // mouse down
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:
		{
			if (WM_LBUTTONDOWN == message)
			{
				auto lastSelected = this->MostParent->Selected;
				this->MostParent->Selected = this;
				if (lastSelected && lastSelected != this)
				{
					lastSelected->SingleUpdate();
				}

				if (xof < this->Width - 8)
				{
					auto undermouseindex = GetGridViewUnderMouseItem(xof, yof, this);
					if (undermouseindex.y < this->Rows.Count && undermouseindex.x < this->Colunms.Count)
					{
						if (this->Colunms[undermouseindex.x].Type == ColumnType::Check)
						{
							this->Rows[undermouseindex.y].Cells[undermouseindex.x].Tag = __int64(this->Rows[undermouseindex.y].Cells[undermouseindex.x].Tag == 0);
							this->OnGridViewCheckStateChanged(this, undermouseindex.x, undermouseindex.y, this->Rows[undermouseindex.y].Cells[undermouseindex.x].Tag != 0);
						}
						else
						{
							this->SelectedColunmIndex = undermouseindex.x;
							this->SelectedRowIndex = undermouseindex.y;
						}
					}
				}
				else
				{
					this->InScroll = true;
					SetScrollByPos(yof);
				}
			}
			MouseEventArgs event_obj = MouseEventArgs(FromParamToMouseButtons(message), 0, xof, yof, HIWORD(wParam));
			this->OnMouseDown(this, event_obj);
			this->SingleUpdate();
		}
		break;
		case WM_LBUTTONUP: // mouse up
		case WM_RBUTTONUP:
		case WM_MBUTTONUP:
		{
			this->InScroll = false;
			if (WM_LBUTTONUP == message && this->MostParent->Selected == this)
			{
				MouseEventArgs event_obj = MouseEventArgs(FromParamToMouseButtons(message), 0, xof, yof, HIWORD(wParam));
				this->OnMouseClick(this, event_obj);
			}
			MouseEventArgs event_obj = MouseEventArgs(FromParamToMouseButtons(message), 0, xof, yof, HIWORD(wParam));
			this->OnMouseUp(this, event_obj);
			this->SingleUpdate();
		}
		break;
		case WM_LBUTTONDBLCLK: // mouse double click
		{
			MouseEventArgs event_obj = MouseEventArgs(FromParamToMouseButtons(message), 0, xof, yof, HIWORD(wParam));
			this->OnMouseDoubleClick(this, event_obj);
			this->SingleUpdate();
		}
		break;
		case WM_KEYDOWN: // keyboard down
		{
			if (wParam == VK_RIGHT)
			{
				SelectedColunmIndex += 1;
				if (SelectedColunmIndex >= this->Colunms.Count)
					SelectedColunmIndex = this->Colunms.Count - 1;
			}
			else if (wParam == VK_LEFT)
			{
				SelectedColunmIndex -= 1;
				if (SelectedColunmIndex < 0)
					SelectedColunmIndex = 0;
			}
			else if (wParam == VK_DOWN)
			{
				SelectedRowIndex += 1;
				if (SelectedRowIndex >= this->Rows.Count)
					SelectedRowIndex = this->Rows.Count - 1;
			}
			else if (wParam == VK_UP)
			{
				SelectedRowIndex -= 1;
				if (SelectedRowIndex < 0)
					SelectedRowIndex = 0;
			}
			int rc = GetGridViewRenderRowCount(this) - 1;
			if (SelectedRowIndex < this->ScrollRowPosition)
				this->ScrollRowPosition = SelectedRowIndex;
			if (SelectedRowIndex > this->ScrollRowPosition + rc)
				this->ScrollRowPosition += 1;
			KeyEventArgs event_obj = KeyEventArgs((Keys)(wParam | 0));
			this->OnKeyDown(this, event_obj);
			this->SingleUpdate();
		}
		break;
		case WM_KEYUP: // keyboard up
		{
			KeyEventArgs event_obj = KeyEventArgs((Keys)(wParam | 0));
			this->OnKeyUp(this, event_obj);
		}
		break;
		}
		return true;
	}
};